package top.huhuiyu.teachproject.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import top.huhuiyu.teachproject.base.BaseDataResult;
import top.huhuiyu.teachproject.base.BaseResult;
import top.huhuiyu.teachproject.dao.*;
import top.huhuiyu.teachproject.entity.*;
import top.huhuiyu.teachproject.exception.AppException;
import top.huhuiyu.teachproject.service.AuthService;
import top.huhuiyu.teachproject.service.RedisService;
import top.huhuiyu.teachproject.utils.AuthInfoUtils;
import top.huhuiyu.teachproject.utils.JwtUtils;
import top.huhuiyu.teachproject.utils.Utils;
import top.huhuiyu.teachproject.vo.UserInfo;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Optional;
import java.util.UUID;

/**
 * 认证相关服务实现
 *
 * @author 胡辉煜
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AuthServiceImpl implements AuthService {

  private final TransactionTemplate transactionTemplate;
  private final TbUserDAO tbUserDAO;
  private final TbUserInfoDAO tbUserInfoDAO;
  private final TbRoleDAO tbRoleDAO;
  private final TbRoleActionsDAO tbRoleActionsDAO;
  private final TbActionsDAO tbActionsDAO;

  private final RedisService redisService;


  @Override
  public UserInfo queryById(Integer uid) {
    if (uid == null) {
      return null;
    }
    UserInfo info = new UserInfo();
    // 基本信息
    info.setTbUser(tbUserDAO.selectById(uid));
    if (info.getTbUser() == null) {
      return null;
    }
    // 附加信息
    info.setTbUserInfo(tbUserInfoDAO.selectById(uid));
    // 角色信息
    info.setTbRole(tbRoleDAO.selectById(info.getTbUser().getRole()));
    return info;
  }

  @Override
  public TbUser queryByName(String username) {
    return tbUserDAO.queryByName(username);
  }

  @Transactional
  @Override
  public BaseResult reg(TbUser user) {
    // 用户名存在检测
    TbUser check = queryByName(user.getUsername());
    if (check != null) {
      return BaseResult.fail("用户名已经存在");
    }
    // 密码md5加密
    String pwd = user.getPassword();
    String salt = Utils.makeSalt();
    pwd = Utils.saltMd5(pwd, salt);
    user.setPassword(pwd);
    user.setSalt(salt);
    // 默认角色和accesskey和accessid
    user.setRole(AuthService.ROLE_USER);
    String accessid = UUID.randomUUID().toString();
    accessid = Base64.getEncoder().encodeToString(accessid.getBytes(StandardCharsets.UTF_8));
    user.setAccessId(accessid);
    user.setAccessKey(UUID.randomUUID().toString());
    // 添加基本信息
    int i = tbUserDAO.insert(user);
    if (i != 1) {
      throw AppException.getInstance("基本信息添加失败，请稍候重试");
    }
    // 附加用户信息
    TbUserInfo info = new TbUserInfo();
    info.setUid(user.getUid());
    info.setCipher(UUID.randomUUID().toString());
    i = tbUserInfoDAO.insert(info);
    if (i != 1) {
      throw AppException.getInstance("附加信息添加失败，请稍后重试");
    }
    return BaseResult.ok("用户注册成功");
  }

  @Override
  public BaseDataResult<UserInfo> login(TbUser user) {
    AuthInfo authInfo = AuthInfoUtils.authInfo();
    // 密码错误校验的key
    String key = String.format("%s_pwd_error", authInfo.getIp());
    Integer count = redisService.get(key, Integer.class);
    count = Optional.ofNullable(count).orElse(0);
    if (count >= 10) {
      return BaseDataResult.failNoData("登录失败次数过多，请稍候重试");
    }
    TbUser check = queryByName(user.getUsername());
    // 名称检测
    if (check == null) {
      return BaseDataResult.failNoData("用户不存在");
    }
    // 是否删除的校验
    if (AuthService.DELETED.equalsIgnoreCase(check.getDeleted())) {
      return BaseDataResult.failNoData("用户被禁用，无法登录");
    }
    // 密码校验
    String pwd = Utils.saltMd5(user.getPassword(), check.getSalt());
    log.debug("密码信息；{},{},{},{}", check.getPassword(), pwd, user.getPassword(), check.getSalt());
    if (!check.getPassword().equalsIgnoreCase(pwd)) {
      // 记录密码错误的次数
      redisService.save(key, count + 1, 20 * 1000);
      return BaseDataResult.failNoData("密码错误");
    }
    // 生成token信息
    String token = JwtUtils.makeUserIdToken(check.getUid());
    authInfo.setToken(token);
    authInfo.setUserId(check.getUid());
    // 完整用户信息
    UserInfo info = queryById(check.getUid());
    BaseDataResult<UserInfo> result = BaseDataResult.ok("登录成功", info.processViewData());
    result.setToken(token);
    return result;
  }

  @Override
  public BaseDataResult<UserInfo> loginInfo() {
    AuthInfo info = AuthInfoUtils.authInfo();
    Integer uid = JwtUtils.parseUserIdToken(info.getToken());
    info.setUserId(uid);
    UserInfo user = this.queryById(info.getUserId());
    if (user == null) {
      return BaseDataResult.failNoData("用户未登录");
    } else {
      return BaseDataResult.ok("", user.processViewData());
    }
  }

  @Override
  public boolean checkAuth() {
    AuthInfo authInfo = AuthInfoUtils.authInfo();
    // 获取路径信息
    QueryWrapper<TbActions> tbActionsQueryWrapper = new QueryWrapper<>();
    tbActionsQueryWrapper.eq("url", authInfo.getMappingPath());
    tbActionsQueryWrapper.eq("method", authInfo.getMethod());
    TbActions actions = tbActionsDAO.selectOne(tbActionsQueryWrapper);
    // 不在数据库中的地址无需校验权限
    if (actions == null) {
      log.debug("地址不在数据库管理中");
      return true;
    }
    log.debug("地址信息：{}", actions);
    // 解析token对应的uid
    Integer uid = JwtUtils.parseUserIdToken(authInfo.getToken());
    authInfo.setUserId(uid);
    // 获取登录用户信息
    TbUser user = tbUserDAO.selectById(authInfo.getUserId());
    // 必须是登录状态
    if (user == null) {
      log.debug("当前用户没有登录");
      return false;
    }
    log.debug("当前用户角色id：{}", user.getRole());
    // 获取角色是否用于api的权限
    QueryWrapper<TbRoleActions> tbRoleActionsQueryWrapper = new QueryWrapper<>();
    tbRoleActionsQueryWrapper.eq("rid", user.getRole());
    tbRoleActionsQueryWrapper.eq("aid", actions.getAid());
    TbRoleActions tbRoleActions = tbRoleActionsDAO.selectOne(tbRoleActionsQueryWrapper);
    if (tbRoleActions == null) {
      log.debug("权限校验失败");
      return false;
    }
    log.debug("权限信息：{}", tbRoleActions);
    return true;
  }
}
